package cn.com.idmy.auth.session;

import cn.com.idmy.auth.Device;
import cn.com.idmy.base.util.Assert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.dromara.hutool.core.text.StrUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.function.Consumer;

import static cn.com.idmy.auth.AuthConfig.NEVER_EXP;
import static cn.com.idmy.auth.context.Contexts.accountType;

@Data
@NoArgsConstructor
@Accessors(fluent = true)
public class SessionManager<ID> {
    @NotNull
    protected volatile SessionConfig sessionConfig = new SessionConfig();
    @NotNull
    protected volatile SessionDao<Session<ID>> sessionDao = new SessionDaoDefaultImpl<>();
    @NotNull
    protected volatile SessionStrategy<ID> sessionStrategy = new SessionStrategy<>();

    public synchronized void init() {
        Assert.notNull(sessionConfig, "sessionConfig 不能为空");
        Assert.notNull(sessionDao, "sessionDao 不能为空");
        Assert.notNull(sessionStrategy, "sessionStrategy 不能为空");
        sessionDao.destroy();
        sessionDao.init();
    }

    @NotNull
    public String cacheKey(@NotNull ID id) {
        return StrUtil.format("{}:session:{}", accountType(), id);
    }

    @NotNull
    public Session<ID> create(@NotNull ID id, @Nullable Consumer<Session<ID>> append) {
        Session<ID> session = sessionStrategy.creator().apply(id);
        if (append != null) { // 必须提前收集因为需要保存到缓存中
            append.accept(session);
        }
        session.id(id);
        session.accountType(accountType());
        session.sessionManager(this);
        // todo session timeout
        sessionDao.update(cacheKey(id), session, 1000);
        return session;
    }

    public void update(@NotNull Session<ID> session) {
        sessionDao.update(cacheKey(session.id()), session);
    }

    public void remove(@NotNull ID id) {
        sessionDao.remove(cacheKey(id));
    }

    @Nullable
    public Session<ID> get(@NotNull ID id) {
        Session<ID> session = sessionDao.get(cacheKey(id));
        if (session == null) {
            return null;
        } else {
            session.sessionManager(this);
            return session;
        }
    }

    public long getExp(@NotNull ID id) {
        return sessionDao.getExp(cacheKey(id));
    }

    public void getExp(@NotNull ID id, long exp) {
        sessionDao.getExp(cacheKey(id), exp);
    }

    /**
     * 修改此Session的最小剩余存活时间 (只有在 Session 的过期时间低于指定的 minTimeout 时才会进行修改)
     */
    public void minExp(@NotNull ID id, long exp) {
        var min = swapExp(exp);
        var curr = swapExp(getExp(id));
        if (curr < min) {
            getExp(id, exp);
        }
    }

    /**
     * 修改此Session的最大剩余存活时间 (只有在 Session 的过期时间高于指定的 maxTimeout 时才会进行修改)
     */
    public void maxExp(@NotNull ID id, long exp) {
        var max = swapExp(exp);
        var curr = swapExp(getExp(id));
        if (curr > max) {
            getExp(id, exp);
        }
    }

    /**
     * value为 -1 时返回 Long.MAX_VALUE，否则原样返回
     */
    private long swapExp(long value) {
        return value == NEVER_EXP ? Long.MAX_VALUE : value;
    }

    @NotNull
    public Session<ID> getOrCreate(ID id) {
        Session<ID> session = get(id);
        return Objects.requireNonNullElseGet(session, () -> create(id, null));
    }

    public void replace(@NotNull Object id, @NotNull Device device) {
    }
}
